iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
1
自我挑戰組

非本科之30天Ruby / Rails學習筆記系列 第 5

Day5: yield在method裡的角色

  • 分享至 

  • xImage
  •  

yield的中文翻譯有很多意思,在程式裡可以翻做「讓」的意思。

那在程式裡,到底是「讓」出什麼東西呢?

p.s不知道為何打到這裡腦袋突然出現扶老太太過馬路

yield

下面例子say_something方法,下方有個掛載的block,yield的作用就是「將控制權暫時讓給Block」之後,再繼續回來執行程式。

def say_something
  puts 'This is start'
  yield
  puts 'This is end'
end

say_something { puts 'Block aboard!'}

# => This is start
#    Block aboard!
#    This is end

程式執行的流程:
從第2行印出'This is start',再往下時遇到yield,暫時將控制權移交給第7行印出'Block aboard!'後,又回到第4行印出'This is end'。

下面例子其實跟上面雷同,只是多一個class而已。

class Hero
  def heart_beating
    yield
  end
end

king = Hero.new
king.heart_beating { p '帝王引擎' }

# => 帝王引擎

yield後面可以帶一個或以上的參數

在yield的後面可以自帶參數,延續上面的例子

class Hero
  def heart_beating
    yield
  end

  def one_punch
    yield '反覆橫跳'
  end
end

king = Hero.new
king.heart_beating { p '帝王引擎' }
# => 帝王引擎

saitama = Hero.new
saitama.one_punch {|skill| p "必殺認真系列..#{skill}" }
# => "必殺認真系列..反覆橫跳"

同一個method裡面塞多個yield

class Hero
  def heart_beating
    yield
  end

  def one_punch
    yield '反覆橫跳'
    yield '連續普通拳'
    yield '認真拳'
  end
end

king = Hero.new
king.heart_beating { p '帝王引擎' }
# => 帝王引擎

saitama = Hero.new
saitama.one_punch {|skill| p "必殺認真系列..#{skill}" }
# => "必殺認真系列..反覆橫跳"
#    "必殺認真系列..連續普通拳"
#    "必殺認真系列..認真拳"

讓程式判斷何時要yeild

Ruby有內建一個方法 block_given?

def optional_block
  yield('還我漂漂拳') if block_given?
end

p optional_block # => nil
optional_block {|x| puts "#{x}" } # => 還我漂漂拳

如果有給block,就yield控制權出去給block。

yield後面可以帶多個參數

def calculator(a, b)
  yield(a, b)
end

puts calculator(5, 6) { |a, b| a + b } # => 11
puts calculator(5, 6) { |a, b| a - b } # => -1


def yield_with_arguments
  yield "巴達獸進化~~~~~~~"

  last_name = 'John'
  first_name = 'Cena'
  yield(last_name, first_name)
end

yield_with_arguments { |l, f| puts "#{l} #{f}" } 
# => 巴達獸進化~~~~~~~
#    John Cena

巴達獸進化

yield組合技

範例一:
第7行的i被yeild出去找了第11行的i,x則是實體變數@v用each方法印出陣列中的數字。

class Map 
  def initialize
    @v = [1, 2, 3, 4]
  end

  def each_print
    @v.each{|i| puts yield i } if block_given?
  end
end

i = "I can count number"
a_obj = Map.new
a_obj.each_print{|x| "#{i} #{x} " }

# =>
# I can count number 1 
# I can count number 2 
# I can count number 3 
# I can count number 4 

範例二:
針對第4行中的counter去yield給method外部的block,且first給預設值=1。可以看到給first參數值,在第4行的counter就會走block中的方式,且從給的值開始往下走。
(備註:next方法可以找下一個數字或字母)

def list(array, first = 1)
  counter = first
  array.each do |item|
    puts "#{yield counter}. #{item}"
    counter = counter.next
  end
end


list(["a","b","c"]) { |ary| ary }
list(["a","b","c"], 100) { |ary| ary }
list(["Ruby", "Is", "Fun"], "A") { |ary| ary }

#output
# 1. a
# 2. b
# 3. c

# 100. a
# 101. b
# 102. c

# A. Ruby
# B. Is
# C. Fun

總結:

yield就是暫時把控制權交棒給method外的 Block,等 Block 執行結束後再把控制權交回來method,並繼續往下走到程式結束。

yield的使用時機,可能因為做的練習跟專案還不夠多,暫時還不確定到底會在哪個情境使用到yield,但可以從上面幾的範例感受到,如果要針對method內某個變數的值去做改變的話,可用yield的方式將控制權讓出給寫在method外的block去做運算。

參考資料:
I don't really understand what is this yield
Blocks and yields in Ruby
Mastering Ruby Blocks in Less Than 5 Minutes
The Ultimate Guide to Blocks, Procs & Lambdas
The yield Keyword in Ruby
為自己學Ruby on Rails
活用Ruby裡的yield

“Most of the important things in the world have been accomplished by people who have kept on trying when there seemed to be no hope at all.”

– Dale Carnegie, Motivational Expert

本文同步發佈於: https://louiswuyj.tw/


上一篇
Day4: Block程式碼區塊及Proc, Lambda差異
下一篇
Day6: 類別(Class)與模組(Module)的差異
系列文
非本科之30天Ruby / Rails學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言